#!/usr/bin/python

#Imports
import os
import sys
import ConfigParser
from time import localtime, strftime
import shutil
from functions import count_total_bytes

import pygtk

#Command line argument variables
debug = 0 #Prints debugging information when equal to 1
debug_huge = 0 #Prints ALOT of debugging information when equal to 1
simulate = 0 #Disables writing to dics when equal to 1
recursive = "1" #Restoring from directory is recursive

#Variables
errors = [] #Stores all errors
files_total_bytes = 0 #The total size, of all files
files_counter_bytes = 0 #The counter, to be compared with files_total_bytes
folder = "" #The backup folder/file path

#Constants
Version = "0.2.6"

################################################################################
########################## READ COMMAND LINE ARGUMENTS #########################
################################################################################

args = sys.argv
while len(args) > 1 :
	arg = args.pop(1)
	if arg == "-d" or arg == "--debug" :
		print "Running with the show debug information on"
		debug = 1
	elif arg == "--debug-huge" :
		print "Running with the show ALOT of debug information on"
		debug = 1
		debug_huge = 1
	elif arg == "-h" or arg == "--help" or arg == "-v" or arg == "--version" :
		print "Usage: restore.py [FLAGS] PATH"
		print ""
		print "The script accepts the following arguments:"
		print ""
		print "Information flags:"
		print "  -h, --help                        Displays this help text."
		print "  -s, --simulate                    Run in simulation mode."
		print "  -d, --debug                       Display debug information."
		print "      --debug-huge                  Display ALOT of debug information."
		print ""
		print "Restore flags:"
		print "      --not-recursive               Restoring from a directory will not be recursively"
		print ""
		print "If PATH is a directory the entire directory, including subdirectories, will be  restored."
		print "If PATH is a file the single will be restored."
		print ""
		print "Linux hard backup tool by Jonas Hansen. Current version: " + Version
		print "http://code.google.com/p/linux-hard-backup-tool/"
		print ""
		exit(0)
	elif arg == "-s" or arg == "--simulate" :
		print "Running in SIMULATION mode: No changes will be made to the disc"
		simulate = 1
	elif arg == "--not-recursive" :
		recursive = "0"
	elif not arg[0] == "-" :
		folder = arg
	else :
		print "ERROR: Unknown flag: " + arg
		exit(1)

################################################################################
############################## PREPARE VARIABLES ###############################
################################################################################

if folder == "" :
	print "ERROR: PLEASE INPUT BACKUP PATH"
	exit(1)

if not os.path.exists(folder) :
	print "ERROR: PATH DOESN'T EXISTS"
	exit(1)

################################################################################
############################# PATH IS A DIRECTORY ##############################
################################################################################

if os.path.isdir(folder) :
	if folder[-1] != "/" : #Check for ending '/'
		folder = folder + "/" #Add an ending '/'
	folderOrg = folder #Store the current folder for later use

	if debug == 1 :
		print "Backup directory: " + folder

################################################################################
################################ PATH IS A FILE ################################
################################################################################

elif os.path.isfile(folder) :
	folderOrg = os.path.split(folder)[0] + "/"
	filename = os.path.split(folder)[1]
	if debug == 1 :
		print "Restoring a file!"
else :
	print "ERROR: PATH IS NEITHER DIRECTORY OR FILE. Don't know how that happened..."
	exit(1)

################################################################################
############################### READ CONFIG FILE ###############################
################################################################################

config = ConfigParser.RawConfigParser() #Prepare config object

if os.path.exists(folderOrg + ".backup") : #Checking for configuration file
	if debug_huge == 1 :
		print "Backup config file: " + folderOrg + ".backup exists"
	config.read(folderOrg + ".backup") #Read from backup config file
	if debug_huge == 1 :
		print "Read from config file: " + folderOrg + ".backup"
	if config.has_option("backup", "source") :
		if debug_huge == 1 :
			print "Read 'source' from config file: " + folderOrg + ".backup"
		folderSource = config.get("backup", "source")
		if folderSource[-1] != "/" :
			folderSource = folderSource + "/"  #Add an ending '/' if it's not already there
		folderSourceOrg = folderSource
	else :
		#WORK NEEDED! Show some kind of error
		print "ERROR: Unable to find source directory!"
		exit(1)
else :
	if debug_huge == 1 :
		print "ERROR: Backup config file: " + folderOrg + ".backup DOESN'T exists"
	#WORK NEEDED! Show some kind of error
	print "ERROR: Unable to find source directory!"
	exit(1)

if debug == 1 :
	print "Source directory: " + folderSource

################################################################################
############################# PATH IS A DIRECTORY ##############################
################################################################################

if os.path.isdir(folder) :
	folders = [folder]
	foldersSource = [folderSource]
	files_total_bytes = count_total_bytes(folders, recursive, [])
	folders = [folderOrg]

################################################################################
########################## DIRECTORY RESTORE PROCESS ###########################
################################################################################

	while len(folders) > 0 : #WORK NEEDED! Show some kind of status...
		folder = folders.pop(0)
		folderSource = foldersSource.pop(0)

		if simulate == 0 :
			if debug_huge == 1 :
				print "Creating source directory: " + folderSource
			fileerror = 1
			try :
				if not os.path.exists(folderSource) :
					os.makedirs(folderSource)
				fileerror = 0
			except (IOError, os.error), why : #catch the Error
				errors.append((folderSource, folderSource, str(why)))
			except Error, err :
				errors.extend(err.args[0])
			except :
				print "Unexpected error:", sys.exc_info()[0]
			if fileerror == 1 :
				print "ERROR: Unable to create source directory"
				exit(1)

		else :
			print "Creating source directory: " + folderSource

		configBak = ConfigParser.RawConfigParser()
		if os.path.exists(folder + ".backup") :
			configBak.read(folder + ".backup")
			if configBak.has_option("backup", "files") :
				i = int(configBak.get("backup", "files"))
			else :
				i = 0
		else :
			i = 0

		for n in range(1, i + 1) :
			if configBak.has_option("backup", "file" + str(n)) :
				filename = configBak.get("backup", "file" + str(n))
				if not os.path.islink(folder + filename) :
					if simulate == 0 :
						try :
							files_counter_bytes += os.stat(folder + filename).st_size
							percentage_done = round(files_counter_bytes*100.0/files_total_bytes,1) #Calculate how much done (in bytes), in percent
							if debug == 0 :
								print " ", percentage_done, " %\r",
								sys.stdout.flush()
							else :
								print percentage_done
							if debug == 1 and debug_huge == 0 :
								print "Working with file: " + folderSource + filename
							if os.path.exists(folderSource + filename) :
								os.remove(folderSource + filename)
								if debug_huge == 1 :
									print "Deleting: " + folderSource + filename
							shutil.copy2(folder + filename, folderSource + filename)
							if debug_huge == 1 :
								print "Copy: " + folder + filename + " > " + folderSource + filename
							if configBak.has_option("backup", "filemode" + str(n)) :
								os.chmod(folderSource + filename, int(configBak.get("backup", "filemode" + str(n))) & 511)
								if debug_huge == 1 :
									print "Setting file permissions: " + folderSource + filename + " to: " + str(int(configBak.get("backup", "filemode" + str(n))) & 511)
							else :
								os.chmod(folderSource + filename, 292) #Unknown permissions, setting to read-only
								if debug_huge == 1 :
									print "Setting file permissions: " + folderSource + filename + " to: " + str(292) + " (READ-ONLY)"
							fileerror = 0 #No file error
						except (IOError, os.error), why : #catch the Error
							errors.append((folder + filename, folderSource + filename, str(why)))
						except Error, err :
							errors.extend(err.args[0])
						except :
							print "Unexpected error:", sys.exc_info()[0]
					else :
						print "Copy: " + folder + filename + " > " + folderSource + filename
				else :
					if os.path.commonprefix([os.path.realpath(folder + filename),folderOrg]) != "/" :
						#This is a symbolic link to a file, with a releative path
						if simulate == 0 :
							try :
								tmp = folderSourceOrg + os.path.relpath(os.path.realpath(folder + filename), os.path.realpath(folderOrg))
								if os.path.exists(folderSource + filename) :
									os.remove(folderSource + filename)
									if debug_huge == 1 :
										print "Deleting link: " + folderSource + filename
								os.symlink(tmp, folderSource + filename)
								if debug_huge == 1 :
									print "Creating link: " + folderSource + filename + " to: " + tmp
							except (IOError, os.error), why : #catch the Error
								errors.append((folderSource + filename, tmp, str(why)))
							except Error, err :
								errors.extend(err.args[0])
							except :
								print "Unexpected error:", sys.exc_info()[0]

							try :
								if configBak.has_option("backup", "filemode" + str(n)) :
									os.chmod(folderSource + filename, int(configBak.get("backup", "filemode" + str(n))) & 511)
									if debug_huge == 1 :
										print "Setting file permissions: " + folderSource + filename + " to: " + str(int(configBak.get("backup", "filemode" + str(n))) & 511)
								else :
									os.chmod(folderSource + filename, 292) #Unknown permissions, setting to read-only
									if debug_huge == 1 :
										print "Setting file permissions: " + folderSource + filename + " to: " + str(292) + " (READ-ONLY)"
							except (IOError, os.error), why : #catch the Error
								errors.append((folderSource + filename, tmp, str(why)))
							except Error, err :
								errors.extend(err.args[0])
							except :
								print "Unexpected error:", sys.exc_info()[0]
						else :
							print "Creating link: " + folderSource + filename + " to: " + tmp
					else :
						#This is a symbolic link to a file, with a absolute path
						if simulate == 0 :
							try :
								if os.path.exists(folderSource + filename) :
									os.remove(folderSource + filename)
									if debug_huge == 1 :
										print "Deleting link: " + folderSource + filename
								os.symlink(os.path.realpath(folder + filename),folderSource + filename)
								if debug_huge == 1 :
									print "Creating link: " + folderSource + filename + " to: " + os.path.realpath(folder + filename)
							except (IOError, os.error), why : #catch the Error
								errors.append((folderSource + filename, tmp, str(why)))
							except Error, err :
								errors.extend(err.args[0])
							except :
								print "Unexpected error:", sys.exc_info()[0]
						else :
							print "Creating link: " + folderSource + filename + " to: " + os.path.realpath(folder + filename)

		if recursive == "1" :
			fileerror = 1
			try :
				files=os.listdir(folder)
				fileerror = 0
			except (IOError, os.error), why : #catch the Error
				errors.append((folder, folder, str(why)))
			except Error, err :
				errors.extend(err.args[0])
			except :
				print "Unexpected error:", sys.exc_info()[0]
			if fileerror == 1 :
				print "ERROR: Unable to list directory '" + folder + "', continuing without it!"
			else :
				for filename in files :
					if os.path.isdir(folder + filename) :
						if not os.path.islink(folder + filename) :
							if debug == 1 :
								print "Working with folder: " + folder + filename + "/"
							folders.append(folder + filename + "/")
							foldersSource.append(folderSource + filename + "/")
						else :
							if os.path.commonprefix([os.path.realpath(folder + filename),folderOrg]) != "/" :
								#This is a symbolic link to a directory, with a releative path
								if simulate == 0 :
									try :
										tmp = folderSourceOrg + os.path.relpath(os.path.realpath(folder + filename), os.path.realpath(folderOrg))
										if os.path.exists(folderSource + filename) :
											os.remove(folderSource + filename)
											if debug_huge == 1 :
												print "Deleting link: " + folderSource + filename
										os.symlink(tmp, folderSource + filename)
										if debug_huge == 1 :
											print "Creating link: " + folderSource + filename + " to: " + tmp
									except (IOError, os.error), why : #catch the Error
										errors.append((folderSource + filename, tmp, str(why)))
									except Error, err :
										errors.extend(err.args[0])
									except :
										print "Unexpected error:", sys.exc_info()[0]
								else :
									print "Creating link: " + folderSource + filename + " to: " + tmp
							else :
								#This is a symbolic link to a directory, with a absolute path
								if simulate == 0 :
									try :
										if os.path.exists(folderSource + filename) :
											os.remove(folderSource + filename)
											if debug_huge == 1 :
												print "Deleting link: " + folderSource + filename
										os.symlink(os.path.realpath(folder + filename),folderSource + filename)
										if debug_huge == 1 :
											print "Creating link: " + folderSource + filename + " to: " + os.path.realpath(folder + filename)
									except (IOError, os.error), why : #catch the Error
										errors.append((folderSource + filename, tmp, str(why)))
									except Error, err :
										errors.extend(err.args[0])
									except :
										print "Unexpected error:", sys.exc_info()[0]
								else :
									print "Creating link: " + folderSource + filename + " to: " + os.path.realpath(folder + filename)

################################################################################
################################ PATH IS A FILE ################################
################################################################################

elif os.path.isfile(folder) :
	if simulate == 0 :
		if debug_huge == 1 :
			print "Creating source directory: " + folderSource
		fileerror = 1
		try :
			if not os.path.exists(folderSource) :
				os.makedirs(folderSource)
			fileerror = 0
		except (IOError, os.error), why : #catch the Error
			errors.append((folderSource, folderSource, str(why)))
		except Error, err :
			errors.extend(err.args[0])
		except :
			print "Unexpected error:", sys.exc_info()[0]
		if fileerror == 1 :
			print "ERROR: Unable to create source directory"
			exit(1)
	else :
		print "Creating source directory: " + folderSource

	configBak = ConfigParser.RawConfigParser()
	if os.path.exists(folderOrg + ".backup") :
		configBak.read(folderOrg + ".backup")
		if configBak.has_option("backup", "files") :
			i = int(configBak.get("backup", "files"))
	else :
		print "ERROR:Unable to find config file"
		exit(1)

	index = -1
	for n in range(1, i + 1) :
		if configBak.has_option("backup", "file" + str(n)) :
			if configBak.get("backup", "file" + str(n)) == filename :
				index = n
				break
	if debug == 1 :
		print "Config file index: " + index

	if simulate == 0 :
		if index != -1 :
			if debug == 1 and debug_huge == 0 :
				print "Working with file: " + folderSource + filename
			if os.path.exists(folderSource + filename) :
				os.remove(folderSource + filename)
				if debug_huge == 1 :
					print "Deleting: " + folderSource + filename
			shutil.copy2(folderOrg + filename, folderSource + filename)
			if debug_huge == 1 :
				print "Copy: " + folderOrg + filename + " > " + folderSource + filename
			if configBak.has_option("backup", "filemode" + str(index)) :
				os.chmod(folderSource + filename, int(configBak.get("backup", "filemode" + str(index))) & 511)
				if debug_huge == 1 :
					print "Setting file permissions: " + folderSource + filename + " to: " + str(int(configBak.get("backup", "filemode" + str(index))) & 511)
		else :
			print "ERROR: File not found in config file. Setting permissions to read only"
			os.chmod(folderSource + filename, 292)
	else :
		print "Restoring: " + folderOrg + filename + " to: " + folderSource + filename

################################################################################
################################ PRINT ERRORS ##################################
################################################################################

if len(errors) > 0 :
	#WORK NEEDED! Show some kind of error...
	print "ERRORS:"
	print errors

################################################################################
################################# SAY GOODBYE ##################################
################################################################################

if debug == 0 :
	print ""
	print "All done :)"

